/** stepCb 和 endCb 回调函数的参数 */
export type CbParams = {
  settleResultList: PromiseSettledResult<any>[] // 所有异步的结果集
  allResultList: Array<any>, // 处理后的所有异步的结果集
  currentIdx: number, // 当前已完成的异步的索引值
  allIdx: number, // 所有异步的数量
  percentage: number // 异步完成的百分比
}

/**
 * AsyncRecursion 构造函数的参数
 * stepAsyncFnParamsList: Array<any> = [] // 需要执行的异步函数（stepAsyncFn)的参数的集合
 * stepAsyncFn: Function = async () => {} // 单步执行的异步函数
 * stepCb: Function = () => {} // stepAsyncFn 每次执行完后的回调
 * endCb: Function = () => {} // 所有 stepAsyncFn 执行完后或者中断执行后（errBreak===true)的回调
 * stepAsyncNum: number = 1 // 单步并发（stepAsyncFn）函数执行的数量
 * errBreak: boolean = false // stepAsyncFn reject 状态时是否中断继续执行
 * */
interface AsyncRecursionConstructorParams {
  stepAsyncFnParamsList: Array<any>,
  stepAsyncFn: Function,
  stepCb?: Function,
  endCb?: Function,
  stepAsyncNum?: number,
  errBreak?: boolean
}

/** AsyncRecursion 类的实例 */
interface AsyncRecursionInstance {
  start(): Promise<CbParams>
}

/** AsyncRecursion 构造函数 */
interface AsyncRecursionConstructor {
  new(params:AsyncRecursionConstructorParams):AsyncRecursionInstance
}

export class AsyncRecursion implements AsyncRecursionInstance {
  private readonly stepAsyncFnParamsList: Array<any> = [] // 需要执行的异步函数（stepAsyncFn)的参数的集合
  private readonly stepAsyncFn: Function = async () => {} // 单步执行的异步函数
  private readonly stepCb: Function = () => {} // stepAsyncFn 每次执行完后的回调
  private readonly endCb: Function = () => {} // 所有 stepAsyncFn 执行完后或者中断执行后（errBreak===true)的回调
  private readonly stepAsyncNum: number = 1 // 单步并发（stepAsyncFn）函数执行的数量
  private readonly errBreak: boolean = false // stepAsyncFn reject 状态时是否中断继续执行

  currentStartIdx: number = 0 // 当前正在执行的索引
  settleResultList: PromiseSettledResult<any>[] = [] // 所有异步函数执行结果的集合
  startResolve: Function = () => {} // 兼容 非回调函数的写法

  constructor (
    {
      stepAsyncFnParamsList = [],
      stepAsyncFn = async () => {},
      stepCb = () => {},
      endCb = () => {},
      stepAsyncNum = 1,
      errBreak = false
    }:AsyncRecursionConstructorParams
  ) {
    this.stepAsyncFnParamsList = stepAsyncFnParamsList
    this.stepAsyncFn = stepAsyncFn
    this.stepCb = stepCb
    this.endCb = endCb
    this.stepAsyncNum = stepAsyncNum
    this.errBreak = errBreak

    this.currentStartIdx = 0
    this.settleResultList = []

    this.bindThis()
  }

  /** 把方法绑定到实例 */
  private bindThis () {
    const thisFnList = <const>['bindThis', 'isErrBreak', 'curAsyncParams', 'isAsyncFinished', 'startNext', 'stepAsyncEnd', 'allAsyncEnd', 'startRecursionAsync', 'start']
    thisFnList.forEach((key: typeof thisFnList[number]) => {
      this[key] = this[key].bind(this) as any
    })
  }

  /** 有错误时，是否中断 */
  private isErrBreak (curResList:PromiseSettledResult<any>[]) {
    const { errBreak } = this
    const hasErr = undefined === curResList.find(val => val.status === 'fulfilled')
    return hasErr && errBreak
  }

  /** 获取当前并发请求的参数 */
  private curAsyncParams () {
    const { stepAsyncFnParamsList, currentStartIdx, stepAsyncNum } = this
    let currentEndIdx = currentStartIdx + stepAsyncNum
    currentEndIdx = currentEndIdx >= stepAsyncFnParamsList.length ? stepAsyncFnParamsList.length : currentEndIdx
    return {
      paramsList: stepAsyncFnParamsList.slice(currentStartIdx, currentEndIdx),
      currentEndIdx
    }
  }

  /** 是否已执行完成所有的异步 */
  private isAsyncFinished () {
    return this.currentStartIdx >= this.stepAsyncFnParamsList.length
  }

  /** 单次异步执行完后触发 */
  private async stepAsyncEnd (cbParams:CbParams) {
    try {
      await this?.stepCb?.(cbParams)
      // 递归触发 下一个异步
      this.startRecursionAsync()
    } catch (e) {
      // 中断异步
      this.allAsyncEnd(cbParams)
    }
  }

  /** 所有异步执行完后触发 */
  private allAsyncEnd (cbParams:CbParams) {
    this?.endCb?.(cbParams)
    this.startResolve(cbParams)
  }

  /** 递归触发 this.startRecursionAsync() */
  private startNext (curResList:PromiseSettledResult<any>[]) {
    const isAsyncFinished = this.isAsyncFinished()
    const isErrBreak = this.isErrBreak(curResList)
    const allIdx = this.stepAsyncFnParamsList.length

    const { currentStartIdx, settleResultList } = this
    const cbParams:CbParams = {
      settleResultList,
      allResultList: settleResultList.map(item => item.status === 'fulfilled' ? item.value : item.reason),
      currentIdx: currentStartIdx,
      allIdx,
      percentage: Math.ceil((currentStartIdx + 1) / allIdx * 100)
    }

    // 触发所有均结束的回调 和 改变Promise实例的状态
    if (isAsyncFinished || isErrBreak) this.allAsyncEnd(cbParams)

    // 触发单步回调 和 下一个 递归
    if (!isAsyncFinished && !isErrBreak) this.stepAsyncEnd(cbParams)
  }

  /** 开始递归的 异步 */
  private async startRecursionAsync () {
    try {
      const { paramsList, currentEndIdx } = this.curAsyncParams()
      const curResList:PromiseSettledResult<any>[] = await Promise.allSettled(paramsList.map(val => this.stepAsyncFn(val)))
      this.settleResultList.push(...curResList)

      this.currentStartIdx = currentEndIdx

      this.startNext(curResList)
    } catch (e) {
      console.error('发生未知错误', e)
    }
  }

  start (): Promise<CbParams> {
    return new Promise((resolve) => {
      this.startResolve = resolve
      this.startRecursionAsync()
    })
  }
}

/** 创建 AsyncRecursion 实例 */
export const creatAsyncRecursion = (AsyncRecursionConstructor:AsyncRecursionConstructor, AsyncRecursionConstructorParams:AsyncRecursionConstructorParams):AsyncRecursionInstance => {
  return new AsyncRecursionConstructor(AsyncRecursionConstructorParams)
}
